// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

#if GAS || INTEL || MASM || NASM || FAST_FMT
using System;

namespace Iced.Intel {
	/// <summary>
	/// Used by a formatter to resolve symbols
	/// </summary>
	public interface ISymbolResolver {
		/// <summary>
		/// Tries to resolve a symbol. It returns <see langword="true"/> if <paramref name="symbol"/> was updated.
		/// </summary>
		/// <param name="instruction">Instruction</param>
		/// <param name="operand">Operand number, 0-based. This is a formatter operand and isn't necessarily the same as an instruction operand.</param>
		/// <param name="instructionOperand">Instruction operand number, 0-based, or -1 if it's an operand created by the formatter.</param>
		/// <param name="address">Address</param>
		/// <param name="addressSize">Size of <paramref name="address"/> in bytes (eg. 1, 2, 4 or 8)</param>
		/// <param name="symbol">Updated with symbol information if this method returns <see langword="true"/></param>
		/// <returns></returns>
		bool TryGetSymbol(in Instruction instruction, int operand, int instructionOperand, ulong address, int addressSize, out SymbolResult symbol);
	}

	// GENERATOR-BEGIN: SymbolFlags
	// ⚠️This was generated by GENERATOR!🦹‍♂️
	/// <summary>Symbol flags</summary>
	[Flags]
	public enum SymbolFlags : uint {
		/// <summary>No bit is set</summary>
		None = 0x00000000,
		/// <summary>It&apos;s a symbol relative to a register, eg. a struct offset <c>[ebx+some_struct.field1]</c>. If this is cleared, it&apos;s the address of a symbol.</summary>
		Relative = 0x00000001,
		/// <summary>It&apos;s a signed symbol and it should be displayed as <c>-symbol</c> or <c>reg-symbol</c> instead of <c>symbol</c> or <c>reg+symbol</c></summary>
		Signed = 0x00000002,
		/// <summary>Set if <see cref="SymbolResult.SymbolSize"/> is valid</summary>
		HasSymbolSize = 0x00000004,
	}
	// GENERATOR-END: SymbolFlags

	/// <summary>
	/// Created by a <see cref="ISymbolResolver"/>
	/// </summary>
	public readonly struct SymbolResult {
		const FormatterTextKind DefaultKind = FormatterTextKind.Label;

		/// <summary>
		/// The address of the symbol
		/// </summary>
		public readonly ulong Address;

		/// <summary>
		/// Contains the symbol
		/// </summary>
		public readonly TextInfo Text;

		/// <summary>
		/// Symbol flags
		/// </summary>
		public readonly SymbolFlags Flags;

		/// <summary>
		/// Checks whether <see cref="SymbolSize"/> is valid
		/// </summary>
		public bool HasSymbolSize => (Flags & SymbolFlags.HasSymbolSize) != 0;

		/// <summary>
		/// Symbol size if <see cref="HasSymbolSize"/> is <see langword="true"/>
		/// </summary>
		public readonly MemorySize SymbolSize;

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="address">The address of the symbol</param>
		/// <param name="text">Symbol</param>
		public SymbolResult(ulong address, string text) {
			Address = address;
			Text = new TextInfo(text, DefaultKind);
			Flags = SymbolFlags.None;
			SymbolSize = MemorySize.Unknown;
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="address">The address of the symbol</param>
		/// <param name="text">Symbol</param>
		/// <param name="size">Symbol size</param>
		public SymbolResult(ulong address, string text, MemorySize size) {
			Address = address;
			Text = new TextInfo(text, DefaultKind);
			Flags = SymbolFlags.HasSymbolSize;
			SymbolSize = size;
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="address">The address of the symbol</param>
		/// <param name="text">Symbol</param>
		/// <param name="color">Color</param>
		public SymbolResult(ulong address, string text, FormatterTextKind color) {
			Address = address;
			Text = new TextInfo(text, color);
			Flags = SymbolFlags.None;
			SymbolSize = MemorySize.Unknown;
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="address">The address of the symbol</param>
		/// <param name="text">Symbol</param>
		/// <param name="color">Color</param>
		/// <param name="flags">Symbol flags</param>
		public SymbolResult(ulong address, string text, FormatterTextKind color, SymbolFlags flags) {
			Address = address;
			Text = new TextInfo(text, color);
			Flags = flags & ~SymbolFlags.HasSymbolSize;
			SymbolSize = MemorySize.Unknown;
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="address">The address of the symbol</param>
		/// <param name="text">Symbol</param>
		public SymbolResult(ulong address, TextInfo text) {
			Address = address;
			Text = text;
			Flags = SymbolFlags.None;
			SymbolSize = MemorySize.Unknown;
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="address">The address of the symbol</param>
		/// <param name="text">Symbol</param>
		/// <param name="size">Symbol size</param>
		public SymbolResult(ulong address, TextInfo text, MemorySize size) {
			Address = address;
			Text = text;
			Flags = SymbolFlags.HasSymbolSize;
			SymbolSize = size;
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="address">The address of the symbol</param>
		/// <param name="text">Symbol</param>
		/// <param name="flags">Symbol flags</param>
		public SymbolResult(ulong address, TextInfo text, SymbolFlags flags) {
			Address = address;
			Text = text;
			Flags = flags & ~SymbolFlags.HasSymbolSize;
			SymbolSize = MemorySize.Unknown;
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="address">The address of the symbol</param>
		/// <param name="text">Symbol</param>
		/// <param name="flags">Symbol flags</param>
		/// <param name="size">Symbol size</param>
		public SymbolResult(ulong address, TextInfo text, SymbolFlags flags, MemorySize size) {
			Address = address;
			Text = text;
			Flags = flags | SymbolFlags.HasSymbolSize;
			SymbolSize = size;
		}
	}

	/// <summary>
	/// Contains one or more <see cref="TextPart"/>s (text and color)
	/// </summary>
	public readonly struct TextInfo {
		/// <summary>
		/// <see langword="true"/> if this is the default instance
		/// </summary>
		public bool IsDefault => TextArray is null && Text.Text is null;

		/// <summary>
		/// The text and color unless <see cref="TextArray"/> is non-null
		/// </summary>
		public readonly TextPart Text;

		/// <summary>
		/// Text and color or null if <see cref="Text"/> should be used
		/// </summary>
		public readonly TextPart[]? TextArray;

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="text">Text</param>
		/// <param name="color">Color</param>
		public TextInfo(string text, FormatterTextKind color) {
			Text = new TextPart(text, color);
			TextArray = null;
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="text">Text</param>
		public TextInfo(TextPart text) {
			Text = text;
			TextArray = null;
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="text">All text parts</param>
		public TextInfo(TextPart[] text) {
			Text = default;
			TextArray = text;
		}
	}

	/// <summary>
	/// Contains text and colors
	/// </summary>
	public readonly struct TextPart {
		/// <summary>
		/// Text
		/// </summary>
		public readonly string Text;

		/// <summary>
		/// Color
		/// </summary>
		public readonly FormatterTextKind Color;

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="text">Text</param>
		/// <param name="color">Color</param>
		public TextPart(string text, FormatterTextKind color) {
			Text = text;
			Color = color;
		}
	}
}
#endif
